﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using AdaptiveAgents.Tools;

namespace AdaptiveAgents.Formulas
{

    /// <summary>
    /// N agent theoretical tool for calculating agents performance
    /// </summary>
    public class NAgentsTool
    {
        /// <summary>
        /// Results array
        /// </summary>
        private List<ResultRecord> _printArray = new List<ResultRecord>();
        /// <summary>
        /// Property- number of agents
        /// </summary>
        public int NumberOfAgents { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="agents"></param>
        public NAgentsTool(int numberOfAgents)
        {
            NumberOfAgents = numberOfAgents;
        }

        /// <summary>
        /// Run test values
        /// </summary>
        /// <param name="numberOfExamples">Number of random examples to create</param>
        public void Run(int numberOfExamples)
        {
            for (int i = 0; i < numberOfExamples; i++)
            {
                List<double> competenceList =  Generator.GetRandomNumbers(NumberOfAgents, true);
                List<double> epsilonList = Generator.GetRandomNumbers(NumberOfAgents, false);

                AgentGroup ag = new AgentGroup();
                for (int k = 0; k < NumberOfAgents; k++)
                {
                    ag.Add(new AgentData(epsilonList[k], competenceList[k]));
                }



                List<double> results = new List<double>();
                ProbabilityTable m = ag.CreateProbabilityTable();

                for (int k = 0; k < m.Size; k++)
                {
                    results.Add(ag.Solve(CreateProbabilityTable(m, k)));
                }

                ResultRecord resultsRecord = new ResultRecord(ag.Agents, results);

                _printArray.Add(resultsRecord);
            }

            PrintResultToCSVFile();
        }


        /// <summary>
        /// Solves 
        /// </summary>
        /// <param name="competenceList"></param>
        /// <param name="epsilonList"></param>
        /// <returns></returns>
        public List<double> Solve(List<double> competenceList, List<double> epsilonList)
        {
            AgentGroup ag = new AgentGroup();
            for (int k = 0; k < competenceList.Count; k++)
            {
                ag.Add(new AgentData(epsilonList[k], competenceList[k]));
            }

            List<double> results = new List<double>();
            ProbabilityTable m = ag.CreateProbabilityTable();

            for (int k = 0; k < m.Size; k++)
            {
                results.Add(ag.Solve(CreateProbabilityTable(m, k)));
            }

            return results;
        }


        /// <summary>
        /// Creates probability table for chosen agent to pass to N other agents
        /// </summary>
        /// <param name="originalTable"></param>
        /// <param name="chosenAgent"></param>
        /// <returns></returns>
        private ProbabilityTable CreateProbabilityTable(ProbabilityTable originalTable, int chosenAgent)
        {
            ProbabilityTable m2 = (ProbabilityTable)originalTable.Clone();
            for (int i = 0; i < m2.Size; i++)
            {
                if (i == chosenAgent)
                {
                    m2[0, i] = 1;
                }
                else
                {
                    m2[0, i] = 0;
                }

            }

            return m2;
        }



        /// <summary>
        /// Prints Results to csv file
        /// </summary>
        public void PrintResultToCSVFile()
        {
            DataTable printOut = new DataTable("Result_Of_Olga");
            DataRow row;
            ResultRecord tempRec;

            DataColumn cln;

            for (int i = 0; i < NumberOfAgents; i++)
            {
                cln = new DataColumn("Epsilon" + i, typeof(double));
                printOut.Columns.Add(cln);
            }

            for (int i = 0; i < NumberOfAgents; i++)
            {
                cln = new DataColumn("Competence" + i, typeof(double));
                printOut.Columns.Add(cln);
            }
            for (int i = 0; i < NumberOfAgents; i++)
            {
                cln = new DataColumn("Result" + i, typeof(double));
                printOut.Columns.Add(cln);
            }

 
            cln = new DataColumn("OptimalResult", typeof(double));
            printOut.Columns.Add(cln);
            cln = new DataColumn("GreedyResult", typeof(double));
            printOut.Columns.Add(cln);
            cln = new DataColumn("Diff", typeof(double));
            printOut.Columns.Add(cln);
            cln = new DataColumn("Type", typeof(int));
            printOut.Columns.Add(cln);
            cln = new DataColumn("CompetenceDiff", typeof(double));
            printOut.Columns.Add(cln);
            cln = new DataColumn("MaxCompetence", typeof(double));
            printOut.Columns.Add(cln);
            cln = new DataColumn("AverageRemainingCompetence", typeof(double));
            printOut.Columns.Add(cln);
            cln = new DataColumn("AverageRemainingEpsilon", typeof(double));
            printOut.Columns.Add(cln);
            cln = new DataColumn("RankDiff", typeof(int));
            printOut.Columns.Add(cln);

            for (int i = 0; i < _printArray.Count; ++i)
            {
                row = printOut.NewRow();
                tempRec = _printArray[i];

                for (int j = 0; j < tempRec.Results.Count; j++)
                {
                    row["Competence" + j] = tempRec.Agents[j].Competence;
                    row["Epsilon" + j] = tempRec.Agents[j].Epsilon;
                    row["Result" + j] = tempRec.Results[j];
                }

                row["OptimalResult"] = tempRec.OptimalResult;
                row["GreedyResult"] = tempRec.GreedyResult;
                row["Diff"] = tempRec.OptimalResult - tempRec.GreedyResult;
                row["Type"] = tempRec.GetAgentType();

                row["CompetenceDiff"] = tempRec.CompetenceDiff;
                row["MaxCompetence"] = tempRec.MaxCompetence;
                row["AverageRemainingCompetence"] = tempRec.AverageRemainingCompetence;
                row["AverageRemainingEpsilon"] = tempRec.AverageRemainingEpsilon;
                row["RankDiff"] = tempRec.RankDiff;

                printOut.Rows.Add(row);
            }
            String stamp = DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss");
            CSVprinter.WriteToCSV("Result_Of_" + NumberOfAgents + "_Agents_" + stamp + ".csv", printOut);
        }

    }


    class ResultRecord
    {
        public List<AgentData> Agents { get; private set; }
        public List<int> Ranks { get; private set; }
        public List<double> Results { get; private set; }
        public int Best { get; private set; }

        public double OptimalResult { get; private  set; }
        public double GreedyResult { get; private set; }
        public int OptimalAgent { get; private set; }

        // max competence of all agents
        public double MaxCompetence { get; private set; }
        // competence average of all agents except the agent with max competence
        public double AverageRemainingCompetence { get; private set; }
        // epsilon average of all agents except the agent with max competence
        public double AverageRemainingEpsilon { get; private set; }
        // diff between competence rank of best agent and optimal agent
        public int RankDiff { get; private set; }
        // diff between competence of best agent and optimal agent
        public double CompetenceDiff { get; private set; }

        public ResultRecord(List<AgentData> agents, List<double> results)
        {
            Agents = agents;
            Results = results;
            Ranks = new List<int>();

            SetBestAgent();
            GreedyResult = Results[Best];

            // set competence ranks
            SetRanks();

            OptimalResult = 0;
            for (int k = 0; k < Agents.Count; k++)
            {
                if (Results[k] > OptimalResult)
                {
                    OptimalResult = Results[k];
                    OptimalAgent = k;
                }
            }

            RankDiff = Ranks.IndexOf(OptimalAgent) - Ranks.IndexOf(Best);
            CompetenceDiff = Agents[Best].Competence - Agents[OptimalAgent].Competence;
            // set averages
            SetAvarages();
        }

        public void SetAvarages()
        {
            AverageRemainingCompetence = 0;
            AverageRemainingEpsilon = 0;

            for(int i=0; i<Agents.Count; i++)
            {
                if (i != Best)
                {
                    AverageRemainingCompetence += Agents[i].Competence;
                    if (i != 0)
                    {
                        AverageRemainingEpsilon += Agents[i].Epsilon;
                    }
                }
            }
            AverageRemainingCompetence /= (Agents.Count - 1);
            AverageRemainingEpsilon /= (Agents.Count - 1);
        }

        public void SetRanks()
        {
            if(Agents.Count > 0)
            {
                Ranks.Add(0);
            }
            for (int i = 0; i < Agents.Count; i++)
            {
                AgentData agent = Agents[i];
                for (int j = 0; j < Ranks.Count; j++)
                {
                    double currentRankCompetence = Agents[Ranks[j]].Competence;
                    if (agent.Competence > currentRankCompetence)
                    {
                        Ranks.Insert(j, i);
                        break;
                    }
                }
            }
        }

        public void SetBestAgent()
        {
            MaxCompetence = 0;
            Best = 0;
            for (int i = 0; i < Agents.Count; i++)
            {
                AgentData a = Agents[i];
                if (a.Competence > MaxCompetence)
                {
                    MaxCompetence = a.Competence;
                    Best = i;
                }
            }
        }

        public int GetSecondBestAgent()
        {
            return Ranks[1];
        }

        public int GetAgentType()
        {
            if (OptimalResult - GreedyResult > 0)
            {
                int best = Best;
                //int secondBestAgent = GetSecondBestAgent();


                // check type1:
                if (OptimalAgent != 0)
                {
                    AgentData optimalAgent = Agents[OptimalAgent];
                    bool type1 = true;
                    foreach (AgentData a in Agents)
                    {
                        // for each agent with better compenetce
                        if (a.Competence > optimalAgent.Competence)
                        {
                            // check if the epsilon is smaller.
                            // if it is not . than it is not type 1
                            if (a.Epsilon < optimalAgent.Epsilon)
                            {
                                type1 = false;
                            }
                        }
                    }
                    if (type1) return 1;
                }
                else
                {
                    // type 2 : Agent 0 is second best
                    return 2;
                }
                // case 3: other 
                return 3;

            }
            return 0;
        }
    }
}

